package com.platform.boot.security.tenant.member;

import com.platform.boot.security.tenant.TenantsRepository;
import com.platform.boot.utils.BaseAutoToolsUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.relational.core.query.Query;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

/**
 * @author <a href="https://github.com/vnobo">Alex bob</a>
 */
@Service
@RequiredArgsConstructor
public class TenantMembersService extends BaseAutoToolsUtil {
    private final TenantMembersRepository tenantMembersRepository;
    private final TenantsRepository tenantsRepository;

    public Flux<TenantMemberOnly> search(TenantMemberRequest request, Pageable pageable) {
        return super.entityTemplate.select(Query.query(request.toCriteria()).with(pageable), TenantMember.class)
                .flatMap(this::serializeOnly);
    }

    public Mono<Page<TenantMemberOnly>> page(TenantMemberRequest request, Pageable pageable) {
        return search(request, pageable).collectList()
                .zipWith(entityTemplate.count(Query.query(request.toCriteria()), TenantMember.class))
                .map(entityTuples -> new PageImpl<>(entityTuples.getT1(), pageable, entityTuples.getT2()));
    }

    private Mono<TenantMemberOnly> serializeOnly(TenantMember tenantMember) {
        return this.tenantsRepository.findByCode(tenantMember.getTenantCode())
                .map(tenant -> TenantMemberOnly.withTenantMember(tenantMember).tenant(tenant));
    }

    public Flux<TenantMember> operate(TenantMemberRequest request) {
        return Flux.fromStream(request.getUsers().parallelStream())
                .map(user -> TenantMemberRequest.of(request.getTenantCode(), user))
                .flatMap(this::save);
    }

    public Mono<Void> delete(TenantMemberRequest request) {
        return this.tenantMembersRepository.delete(request.toMemberTenant());
    }

    @Transactional(rollbackFor = Exception.class)
    public Mono<TenantMember> save(TenantMember tenantMember) {

        Flux<TenantMember> source = this.tenantMembersRepository.findByUserCode(tenantMember.getUserCode());

        Flux<TenantMember> updateFalse = source.filter(old -> !old.getTenantCode().equals(tenantMember.getTenantCode()))
                .flatMap(old -> {
                    old.setEnabled(false);
                    return this.tenantMembersRepository.save(old);
                });

        Flux<TenantMember> updateTrue = source.filter(old -> old.getTenantCode().equals(tenantMember.getTenantCode()));

        return updateTrue.delayUntil(result -> updateFalse).flatMap(old -> {
            old.setEnabled(true);
            return this.tenantMembersRepository.save(old);
        }).singleOrEmpty().switchIfEmpty(this.tenantMembersRepository.save(tenantMember));
    }
}